package com.quectel.emp.controller.securitypatrol;

import cn.hutool.core.lang.func.LambdaUtil;
import com.quectel.constant.core.securitypatrol.SecurityPatrolEventReportConstants;
import com.quectel.constant.core.securitypatrol.SecurityPatrolTaskConstants;
import com.quectel.constant.core.workorder.WorkOrderConstants;
import com.quectel.constant.global.SystemConstants;
import com.quectel.constant.global.annotation.RequirePermissions;
import com.quectel.core.module.securitypatrol.dto.SecurityPatrolEventReportDto;
import com.quectel.core.module.securitypatrol.dto.SecurityPatrolTaskDto;
import com.quectel.core.module.securitypatrol.dto.SecurityPatrolTaskRouteDto;
import com.quectel.core.module.securitypatrol.service.*;
import com.quectel.core.module.workorder.dto.WorkOrderDto;
import com.quectel.emp.constants.ResourceConstants;
import com.quectel.emp.controller.BaseController;
import com.quectel.emp.interceptor.LoginEmpSession;
import com.quectel.emp.service.workorder.ApiWorkOrderService;
import com.quectel.emp.util.SessionHolder;
import com.quectel.emp.vo.securitypatrol.SecurityPatrolEventReportVo;
import com.quectel.util.common.*;
import com.quectel.util.ex.CheckErrorException;
import com.quectel.util.redis.RedisUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

import static cn.hutool.core.text.CharSequenceUtil.splitTrim;

/**
 * @author bob
 * @email bob.yu@quectel.com
 * @date 2022/5/24 10:58
 */
@RestController
@RequestMapping("securityPatrol")
@Tag(name = "SecurityPatrolController", description = "安保巡检相关的api")
public class SecurityPatrolController extends BaseController {

    @DubboReference
    private SecurityPatrolTaskService securityPatrolTaskService;
    @DubboReference
    private SecurityPatrolEventReportService securityPatrolEventReportService;
    @DubboReference
    private SecurityPatrolSubjectService securityPatrolSubjectService;
    @Autowired
    ApiWorkOrderService apiWorkOrderService;
    @DubboReference
    private SecurityPatrolDeviceSubjectService securityPatrolDeviceSubjectService;
    @DubboReference
    private SecurityPatrolTaskRouteService securityPatrolTaskRouteService;

    /**
     * 安保设备上报限制,根据taskId+taskRouteId
     */
    private static final String SECURITY_PATROL_REPORT_LIMIT_LOCK_BY_TASKID_AND_TASKROUTEID = "SECURITY_PATROL_REPORT_LIMIT_LOCK_BY_TASKID_AND_TASKROUTEID:";
    /**
     * 设备上报间隔
     */
    private static final int SECURITY_PATROL_REPORT_TIME_LIMIT = 10;

    private static final String EXEC_START_TIME = "execStartTime";
    private static final String EXEC_END_TIME = "execEndTime";
    private static final String EXEC_TIME = "execTime";
    private static final String STATUSES = "statuses";

    /**
     * 安保巡检任务分页列表
     */
    private static final String SECURITY_PATROL_REPORT_LIMIT_LOCK_BY_TASKID = "SECURITY_PATROL_REPORT_LIMIT_LOCK_BY_TASKID:";

    @GetMapping("list")
    @Operation(summary = "安保巡检任务分页列表")
    @RequirePermissions(values = {ResourceConstants.SECURITY_PATROL})
    public Response<Page<SecurityPatrolTaskDto>> list(@Parameter(description = "巡检任务状态 0未开始 1进行中 2已结束") @RequestParam(required = false) String statusFlag,
                                                      @Parameter(description = "巡检分类 1.质量巡检 2.安保巡检 3.环境保洁 4.绿化养护") @RequestParam(required = false) Byte category,
                                                      @Parameter(description = "从1开始的页码数") @RequestParam Integer pageNo,
                                                      @Parameter(description = "每页多少条") @RequestParam Integer pageSize) {
        LoginEmpSession loginEmpSession = SessionHolder.getLoginEmpSession();
        Request request = Request.configParams(Request.parseParams(httpServletRequest));
        if (StringUtils.isNotBlank(statusFlag)) {
            request.getParams().put(STATUSES, splitTrim(statusFlag, ","));
        }

        Long deptPersonId = loginEmpSession.getDeptPersonDto().getId();
        request.put(LambdaUtil.getFieldName(SecurityPatrolTaskDto::getExecUserId), deptPersonId);
        request.getParams().put(LambdaUtil.getFieldName(SecurityPatrolTaskDto::getCategory), category);

        List<SecurityPatrolTaskDto> result = null;
        int total = 0;
        // 明天的和今天的当前时间之后的
        // 当前时间  <  任务开始时间  13:58计划开始  现在 13:51  那就是未开始
        if (statusFlag.contains("0")) {
            request.getParams().put(EXEC_START_TIME, EXEC_START_TIME);
            request.getParams().put("execStatus", 0); // 任务肯定是未执行的
        }

        // 当前时间 在 任务开始结束之间
        // 任务状态是未执行 或者 执行中
        if (statusFlag.contains("1")) {
            request.getParams().put(EXEC_TIME, EXEC_TIME);
            request.getParams().remove(STATUSES);
            request.put(STATUSES, splitTrim("0,1", ","));
        }

        // 当前时间 大于 任务结束时间  都是结束
        // 不限制任务状态。时间超了都是超
        if (statusFlag.contains("2")) {
            request.put(EXEC_END_TIME, EXEC_END_TIME);
            request.getParams().remove(STATUSES);
        }
        total = securityPatrolTaskService.queryTotal(request.getParams());
        if (total > 0) {
            request.put("padding", "true");
            result = securityPatrolTaskService.queryList(request.getParams());
        } else {
            result = new ArrayList<>();
        }

        Page<SecurityPatrolTaskDto> page = new Page<>(request.getPageNo(), request.getPageSize(), total, result);
        return Response.<Page<SecurityPatrolTaskDto>>ok().wrap(page);
    }

    /**
     * 根据taskId查看巡检任务详情
     *
     * @param taskId
     * @return
     */
    @GetMapping("queryDetailByTaskId")
    @Operation(summary = "根据taskId查看巡检任务详情")
    @RequirePermissions(values = {ResourceConstants.SECURITY_PATROL})
    public Response<SecurityPatrolTaskDto> queryByTaskId(@Parameter(description = "巡检任务id") @RequestParam Long taskId) {
        SessionHolder.checkEmp();
        // 填充手机号返回给前端展示
        String mobile = SessionHolder.getEmp().getMobile();
        SecurityPatrolTaskDto securityPatrolTaskDto = securityPatrolTaskService.selectByIdPaddingFieldDeviceList(taskId);
        if (StringUtils.isNotEmpty(mobile)) {
            securityPatrolTaskDto.setExecUserMobile(mobile);
        }
        return Response.<SecurityPatrolTaskDto>ok().wrap(securityPatrolTaskDto);
    }

    /**
     * 根据上面返回的点位list接口，传入taskRouteId，查询具体点位的项目上报记录列表
     *
     * @return
     */
    @GetMapping("querySubjectListByTaskRouteId")
    @Operation(summary = "根据任务点位id，查询点位下的上报项目list")
    @RequirePermissions(values = {ResourceConstants.SECURITY_PATROL})
    public Response<List<SecurityPatrolEventReportDto>> list(@RequestParam Long taskRouteId) {
        Request request = Request.configParams(Request.parseParams(httpServletRequest));
        request.put(LambdaUtil.getFieldName(SecurityPatrolEventReportDto::getTaskRouteId), taskRouteId);
        int total = securityPatrolEventReportService.queryTotal(request.getParams());

        List<SecurityPatrolEventReportDto> list = null;
        // 查询点位的上报记录,点位的记录只会全部项目都上报或者都没上报
        if (total > 0) {
            list = securityPatrolEventReportService.queryListWithoutPadding(request.getParams());
        } else {
            list = new ArrayList<>();
        }
        return Response.<List<SecurityPatrolEventReportDto>>ok().wrap(list);
    }

    /**
     * 上报流程
     * 实体包含点位信息 和 项目 list
     * 情况1.
     * 点位是否跳检 根据点位信息的status==2 ，项目list传空。逻辑更新跳检点位下的项目记录的状态为跳检
     * 情况2.
     * 点位不跳检，点位信息execInfo status=-1
     * 项目list非空
     * a.若不需要生成工单 assignWorkOrder=0  工单处理 assignUserId可不填。
     * b.项目list循环更新完后，去更新点位的status
     * 不跳检点位状态只有正常和异常.
     * 若项目list中出现一个异常，点位就是异常;
     * 如果项目list 都是正常，点位status为正常
     *
     * @param reportVo
     * @return
     */
    @PostMapping("report")
    @Operation(summary = "上报记录")
    @RequirePermissions(values = {ResourceConstants.SECURITY_PATROL})
    public Response<Object> report(@RequestBody SecurityPatrolEventReportVo reportVo) {
        LoginEmpSession loginEmpSession = SessionHolder.getLoginEmpSession();
        SecurityPatrolEventReportVo.ExecInfo execInfo = reportVo.getExecInfo();
        if (execInfo == null) {
            return Response.error("点位dto不能为空");
        }
        if (execInfo.getSnapshotImg() != null) {
            JacksonUtils.parseArray(execInfo.getSnapshotImg(), String.class);
        }
        // 判断点位状态是否已经执行过  taskRouteStatus 和 未执行不一致 直接reture
        SecurityPatrolTaskRouteDto taskRouteDto = securityPatrolTaskRouteService.selectById(execInfo.getId());
        if (!SecurityPatrolEventReportConstants.ReportStatusEnum.NOT_CHANGED.getStatus().equals(taskRouteDto.getStatus())) {
            return Response.error("当前点位已上报无需重复上报");
        }

        if (!RedisUtils.setnx(SECURITY_PATROL_REPORT_LIMIT_LOCK_BY_TASKID_AND_TASKROUTEID + execInfo.getTaskId() + SystemConstants.COLON + execInfo.getId(), SystemConstants.YES.toString(), SECURITY_PATROL_REPORT_TIME_LIMIT, TimeUnit.SECONDS)) {
            throw new CheckErrorException("上报过于频繁");
        }
        List<SecurityPatrolEventReportDto> eventReportDtoList = new ArrayList<>();
        SecurityPatrolTaskRouteDto securityPatrolTaskRouteDto = new SecurityPatrolTaskRouteDto();
        Long taskId = reportVo.getExecInfo().getTaskId();

        SecurityPatrolTaskDto securityPatrolTaskDto = securityPatrolTaskService.selectById(taskId);
        securityPatrolTaskRouteDto.setTaskId(taskId);

        //参数校验
        if (securityPatrolTaskDto == null) {
            return Response.error("当前任务不存在");
        }

        if (securityPatrolTaskDto.getExecCount().equals(securityPatrolTaskDto.getRouteCount())) {
            return Response.error("任务已完成,无需重复提交");
        }

        if (SecurityPatrolTaskConstants.TasksStatusEnum.STATUS_EXECUTED.getStatus().equals(securityPatrolTaskDto.getExecStatus())) {
            return Response.error("当前任务不可执行");
        }
        if (!securityPatrolTaskDto.getExecUserId().equals(loginEmpSession.getDeptPersonDto().getId())) {
            return Response.error("员工不可执行该任务");
        }

        // 根据不同的任务类型，填充工单类型
        // 质量巡检
        WorkOrderConstants.CategoryAndType workOrderCategoryAndType = null;

        // 安防巡检
        if (securityPatrolTaskDto.getCategory().equals(SecurityPatrolTaskConstants.TaskCategoryEnum.SECURITY_PATROL.getTaskCategory())) {
            workOrderCategoryAndType = WorkOrderConstants.CategoryAndType.SECURITY_PATROL;
        }

        //校验是否在执行时间
        Date now = new Date();
        String dateStr = new SimpleDateFormat("yyyy-MM-dd").format(now);
        String time = new SimpleDateFormat("HH:mm").format(now);
        if (!Objects.equals(dateStr, securityPatrolTaskDto.getDate()) || time.compareTo(securityPatrolTaskDto.getPeriod().split("-")[0]) < 0 || time.compareTo(securityPatrolTaskDto.getPeriod().split("-")[1]) > 0) {
            return Response.error("当前时间不在该任务的时间段范围之内");
        }

        // 上报点位状态为跳检状态 地址为空- 项目list可以不传,直接去更新
        if (SecurityPatrolEventReportConstants.ReportStatusEnum.STATUS_SKIP_INSPECTION.getStatus().equals(execInfo.getStatus())) {
            securityPatrolTaskRouteDto = CopyUtils.copyObj(execInfo, SecurityPatrolTaskRouteDto.class);
        }

        // 上报点位状态为非跳检 status =  -1  并且 上报的list非空
        // 地址不能为空
        if (SecurityPatrolEventReportConstants.ReportStatusEnum.NOT_CHANGED.getStatus().equals(execInfo.getStatus()) && reportVo.getReportVoList().size() > 0) {

            if (StringUtils.isEmpty(execInfo.getAddress())
                    || execInfo.getLat() == null || execInfo.getLng() == null) {
                return Response.error("上报地址不能为空");
            }

            List<SecurityPatrolEventReportVo.SpEventReportVo> reportVoList = reportVo.getReportVoList();
            if (CollectionUtils.isEmpty(reportVoList)) {
                return Response.error("任务上报记录列表不能为空");
            }

            // 更新reportList
            for (SecurityPatrolEventReportVo.SpEventReportVo spEventReportVo : reportVoList) {
                // 上报记录id 查询记录是否存在。记录都提前生成，必须存在
                SecurityPatrolEventReportDto selectOne = securityPatrolEventReportService.selectById(spEventReportVo.getId());
                if (selectOne == null) {
                    return Response.error("提交的项目不存在");
                }
                SecurityPatrolEventReportDto recordReportVo = CopyUtils.copyObj(spEventReportVo, SecurityPatrolEventReportDto.class);

                // assignUserId 请调用 empapi/workOrder/getServiceUsers 接口获取

                // 上报信息中的工单信息非空 数据填充
                if (SecurityPatrolEventReportConstants.AssignWorkOrderStatusEnum.ASSIGN_WORK_ORDER.getStatus().equals(spEventReportVo.getAssignWorkOrder())) {
                    WorkOrderDto workOrderDto = apiWorkOrderService.patrolReportOrder(
                            loginEmpSession.getDeptPersonDto().getId(),
                            workOrderCategoryAndType,
                            spEventReportVo.getDetail(),
                            spEventReportVo.getSnapshotImg(),
                            spEventReportVo.getAssignUserId(),
                            taskRouteDto.getDeviceId()
                    );
                    recordReportVo.setWorkOrderId(workOrderDto.getId());
                    recordReportVo.setWorkOrderNo(workOrderDto.getOrderCode());
                }

                recordReportVo.setCreateTime(new Date());
                eventReportDtoList.add(recordReportVo);
            }

            securityPatrolTaskRouteDto = CopyUtils.copyObj(reportVo.getExecInfo(), SecurityPatrolTaskRouteDto.class);
        }

        // 处理拼装的数据
        Lock spinLock = RedisUtils.getSpinLock(SECURITY_PATROL_REPORT_LIMIT_LOCK_BY_TASKID + securityPatrolTaskDto.getId());
        spinLock.lock();
        try {
            securityPatrolEventReportService.report(securityPatrolTaskRouteDto, eventReportDtoList);
        } catch (Exception e) {
            throw new CheckErrorException("上报过于频繁");
        } finally {
            spinLock.unlock();
        }

        return Response.ok().wrap("上报完成");
    }


    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("https://dev-smartcity.quectel.com:10443/public-bucket/2021/11/25/74f2a7af7b6342a3be20dcdb66fa6dd6.jpeg");
        String snapshotImg = list.toString();
        if (snapshotImg != null) {
            JacksonUtils.parseArray(snapshotImg, String.class);
        }

    }
}
